package br.com.iswe.gerador.velocity.gerador;

import java.io.File;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;

import br.com.iswe.gerador.velocity.info.InfoEntidadeImpl;
import br.com.iswe.gerador.velocity.info.InfoGerador;

@SuppressWarnings("unchecked")
public abstract class GeradorGenerico extends GeradorBase {

	protected String pastaSaida = null;
	protected GeradorHandler handler = null;
	protected String templateName = null;
	
	protected String basePath = null;
	protected File pathSaida = null;
	protected String arquivoSaida = null;
	protected boolean genereteFile = false; 
	
	protected GeradorGenerico geradorAnterior = null;
	protected List<GeradorGenerico> proximosGeradores = new ArrayList<GeradorGenerico>();
	
	public GeradorGenerico(){}
	
	public GeradorGenerico(String pastaSaida, String templateName, GeradorHandler handler){
		this.pastaSaida 	= pastaSaida;
		this.handler 		= handler;
		this.templateName 	= templateName;
	}
	
	protected InfoGerador infoGerador;

	
	protected static Map<Class, List<Class>> entidadesGeradas = new HashMap<Class, List<Class>>();
	
	protected List<Class> listaEntidades = null;
	
	public void gerarCodigo(InfoEntidadeImpl infoEntidade) throws Exception {
		//verifica se ja foi gerado o artefato para esta entidade
		if(getListaEntidades().contains(infoEntidade.getEntidade())){
			//caso ja exista sai fora.
			return;
		}
		getListaEntidades().add(infoEntidade.getEntidade());
		
		// Primeiro Passo : Cria o contexto com as informacoes necessarias para
		// criar a classe de BO.
		VelocityContext ctx = buildContext(this.infoGerador);
        // Adiciona a info da entidade
		ctx.put(InfoEntidadeImpl.NOME_INFO, infoEntidade);
		
		configVelocityContext(ctx, infoEntidade);
		
		// Segundo Passo : Obtem o diretorio onde sera gerado o fonte de saida.
		basePath = infoGerador.getPastaBaseSaida() + SEPARADOR + pastaSaida;
		pathSaida = new File(basePath + SEPARADOR + infoGerador.getPathFromPackage( handler.getPackageObjeto( infoEntidade ) ));
		arquivoSaida = pathSaida.getAbsolutePath() + SEPARADOR + handler.getNomeArquivo( infoEntidade );
		genereteFile = handler.shouldGenerateFile(infoGerador, arquivoSaida);
		if( genereteFile ){
			
			System.out.println("Generating File: " + arquivoSaida);
			
        	// Verifica se o path ja existe
    		if (!pathSaida.exists()) {
    			pathSaida.mkdirs();
    		}
			// Obtem o template.
    		Template template = null;
			String encoding = getEncoding();
			template = Velocity.getTemplate(templateName);
			// Escreve o arquivo de saida
			PrintWriter wrt = null;
			if(encoding != null){
				wrt = new PrintWriter(arquivoSaida, encoding);
			}
			else{
				wrt = new PrintWriter(arquivoSaida);
			}
			// Gera o documento.
			template.merge(ctx, wrt);
			// Descarrega para o disco.
			wrt.flush();
			wrt.close();
		}
		else{
			System.out.println("Skiping the generation of File: " + arquivoSaida);
		}
        proximosGeradores(infoEntidade);
	}

	/**
	 * @param ctx
	 * @param infoEntidade 
	 */
	protected void configVelocityContext(VelocityContext ctx, InfoEntidadeImpl infoEntidade) {
		//empty .. to be implemented by sub classes
	}

	protected String getEncoding() {
		return null;
	}

	private void proximosGeradores(InfoEntidadeImpl infoEntidade) throws Exception {
		if(proximosGeradores.size() > 0){
			for(GeradorGenerico item : proximosGeradores){
				item.doChain(this, infoGerador, infoEntidade);
			}
        }
	}

	/**
	 * Metodo usado para uma cadeia de geradores.
	 * @param generico
	 * @param infoGerador
	 * @param infoEntidade
	 * @throws Exception 
	 */
	protected void doChain(GeradorGenerico geradorAnterior, InfoGerador infoGerador, InfoEntidadeImpl infoEntidade) throws Exception {
		this.geradorAnterior = geradorAnterior;
		inicio(infoGerador);
		gerarCodigo(infoEntidade);
		fim();
	}

	public void inicio(InfoGerador infoGerador) {
		this.infoGerador = infoGerador;
	}

	public void fim() {
		this.infoGerador = null;
	}

	public void addProximoGerador(GeradorGenerico proximoGrrador) {
		this.proximosGeradores.add(proximoGrrador);
	}

	public boolean shouldGenereteFile() {
		return genereteFile;
	}

	public List<Class> getListaEntidades() {
		this.listaEntidades =  entidadesGeradas.get(this.getClass());
		if(listaEntidades == null){
			listaEntidades = new ArrayList<Class>();
			entidadesGeradas.put(this.getClass(), listaEntidades);
		}
		return listaEntidades;
	}

	
}
